﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Security.Cryptography.X509Certificates;
using System.Security.Cryptography.Pkcs;
using System.Diagnostics;
using System.Text;
using System.IO;

namespace servidor
{
    public class GestorReceber
    {
        private Utilizador utilizador = null;
        private List<Utilizador> utilizadoresExistentes = new List<Utilizador>();
        private string chavePrivadaServidor = "c:/certificados/estg.dei.si.a_gmail.com.pfx";
        private string chavePrivadaPassword = "ei.si";
        private string chavePublicaServidor = "c:/certificados/estg.dei.si.a_gmail.com.cer";

        public byte[] start(byte[] chaveUtilizador, byte[] dados)
        {

            //Carrega lista de utilizadores
            this.loadListaUtilizadores();

            //1 passo, verifica certificado utilizador
            if (!verificaCertificado(chaveUtilizador))
               return respostaNao();

            //2 passo, verifica assinatura e descripta
            string dadosLimpos = verificaDescripta(dados);

            if (dadosLimpos == null)
               return respostaNao();

            //3- passo, gerar objeto Utilizador
            gerarUtilizador(dadosLimpos);
                
            //4- verifica codigos
            if (!verificaCodigos(chaveUtilizador))
            return respostaNao();

            //5- verifica se é quinta-feira
            if (!isQuintaFeira())
                return respostaNao();

            return respostaSim();
        }

        private byte[] respostaNao()
        {

            string res = utilizador.SerialUnico + "#n";
            byte[] enc = encriptarResposta(res);
            byte[] signed = assinarResposta(enc);
            return signed;
        }

        private byte[] respostaSim()
        {
            string res = utilizador.SerialUnico + "#s";
            byte[] enc = encriptarResposta(res);
            byte[] signed = assinarResposta(enc);
            return signed;
        }

        private byte[] encriptarResposta(string dadosLimpos)
        {

            //obter dados a cifrar
            //obter dados
            byte[] data = Encoding.UTF8.GetBytes(dadosLimpos);
            ContentInfo cInfo = new ContentInfo(data);

            //obter certificado
            X509Certificate2 cert = new X509Certificate2(utilizador.ChavePublica);
            CmsRecipient recipient = new CmsRecipient(cert);

            //cifrar
            EnvelopedCms envelope = new EnvelopedCms(cInfo);
            envelope.Encrypt(recipient);

            //obter dados cifrados (PKCS#7)
            return envelope.Encode(); //os dados
        }

        private byte[] assinarResposta(byte[] dadosEncripted)
        {
            ContentInfo cInfo = new ContentInfo(dadosEncripted);

            X509Certificate2 cert = new X509Certificate2(chavePrivadaServidor, chavePrivadaPassword);
            CmsSigner signer = new CmsSigner(cert);
            SignedCms signedCms = new SignedCms(cInfo, false); //false, com dados
            signedCms.ComputeSignature(signer);
            return signedCms.Encode();
        }

        public bool isQuintaFeira()
        {

            DayOfWeek dw = DateTime.Now.DayOfWeek;

            if (dw == DayOfWeek.Thursday)
                return true;

            return false;
        }

        private bool verificaCodigos(byte[] chaveUtilizador)
        {
            GestorSerialize gs = new GestorSerialize();
            
            utilizador.ChavePublica = "c:/certificados/utilizador" + utilizador.SerialUnico + ".cer";
            
            if (checkUserFirstTime())
            {
                guardaDadosFirstTime(chaveUtilizador);
                utilizadoresExistentes.Add(utilizador);
                //updateUtilizadoresNaLista(utilizador, utilizadoresExistentes);
                gs.Save(utilizadoresExistentes);
                return true;
            }

            if (!codigosMinimosParaValidar())
                return false;


            return true;
        }

        //vai verificar se o minimo de codigos valida
        private bool codigosMinimosParaValidar()
        {
            int numValidos = 0;
            Utilizador temp = new Utilizador();

            foreach (Utilizador u in utilizadoresExistentes)
            {
                if (u.SerialUnico == utilizador.SerialUnico)
                    temp = u;
            }

            if (temp.DiskSerial == utilizador.DiskSerial)
                numValidos++;

            if (temp.ProdutoID == utilizador.ProdutoID)
                numValidos++;

            if (temp.SerialBios == utilizador.SerialBios)
                numValidos++;

            if (numValidos > 0)
            {
                utilizadoresExistentes = updateUtilizadoresNaLista(utilizador, utilizadoresExistentes);
                GestorSerialize gs = new GestorSerialize();
                gs.Save(utilizadoresExistentes);
                return true;
            }
            else
            {
                return false;
            }

        }

        private List<Utilizador> updateUtilizadoresNaLista(Utilizador util, List<Utilizador> lista)
        {
            Utilizador temp = new Utilizador();
            int sel = 0;

            for (int i = 0; i < lista.Count; i++)
            {
                if (lista[i].SerialUnico == util.SerialUnico)
                    sel = i;
            }

            lista.RemoveAt(sel);
            lista.Add(util);

            return lista;
        }


        //guarda a chave publica do certificado do utilizador
        private void guardaDadosFirstTime(byte[] chaveUtilizador)
        {
            

            try
            {
                System.IO.FileStream fs = new System.IO.FileStream( utilizador.ChavePublica , System.IO.FileMode.Create, System.IO.FileAccess.Write);

                fs.Write(chaveUtilizador, 0, chaveUtilizador.Length);

                fs.Close();
            }
            catch (Exception ex)
            {
                Console.WriteLine("erro a guardar " + ex.Message);
            }
        }


        private void loadListaUtilizadores()
        {
            GestorSerialize gs = new GestorSerialize();

            if (!File.Exists("c:/certificados/listaUtilizadores.txt"))
                gs.Save(utilizadoresExistentes);
            else
                utilizadoresExistentes = gs.Load();
        }

        //Verifica se é a primeira vez que o utilizador acede ao servico
        private bool checkUserFirstTime()
        {
            if (!File.Exists("c:/certificados/utilizador" + utilizador.SerialUnico + ".cer"))
                return true;
            else
                return false;
        }

        private void gerarUtilizador(string dados)
        {
            utilizador = new Utilizador();
            utilizador = this.partirString(dados);
        }

        public bool verificaCertificado(byte[] chaveUtilizador)
        {
            X509Certificate2 cert = new X509Certificate2(chaveUtilizador);
                return cert.Verify();
        }

        public string verificaDescripta(byte[] dados)
        {
            //Descodificar a mensagem
            SignedCms signedCms = new SignedCms();
            signedCms.Decode(dados);
            

            //verificar a assinatura
            try
            {
                signedCms.CheckSignature(false);
            }
            catch (Exception ex)
            {
                return null;
            }


            //obter o certificado (!!!)
            X509Certificate2 cert = new X509Certificate2(chavePrivadaServidor, chavePrivadaPassword);


            //decifrar
            EnvelopedCms envelope = new EnvelopedCms();
            envelope.Decode(signedCms.ContentInfo.Content);
            //envelope.Decrypt(); //vai a procura automaticamente

            envelope.Decrypt(new X509Certificate2Collection(cert));

            //mostrar a informação
            string dadosLimpos = Encoding.UTF8.GetString(envelope.ContentInfo.Content);
            Debug.WriteLine("dados no servidor:" + dadosLimpos);


            return dadosLimpos;
        }

        public Utilizador partirString(String str)
        {
            try
            {
                List<string> lista = str.Split('#').ToList<string>();

                Utilizador util = new Utilizador();
                int i = 0;

                foreach (var item in lista)
                {
                    if (item.Trim() == "1")
                        util.ProdutoID = lista[i + 1].ToString();

                    if (item.Trim() == "2")
                        util.DiskSerial = lista[i + 1].ToString();

                    if (item.Trim() == "3")
                        util.SerialBios = lista[i + 1].ToString();

                    if (item.Trim() == "4")
                        util.SerialUnico = lista[i + 1].ToString();

                    i++;
                }

                return util;
            }
            catch (Exception ex)
            {
                Console.WriteLine("partirStringIds: " + ex.Message);
                return null;
            }
        }
    }
}